package org.springframework.web.servlet.ajax.json;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Charles Tassoni
 */

public class JsonAjaxReactionImpl implements JsonAjaxReaction {
	private PartialsCore partialsCore;
	private HttpServletRequest request;
	private HttpServletResponse response;
	private Map model;
	
	private StringBuffer javascript = new StringBuffer("");
	
	private LinkedHashMap willBeJson = new LinkedHashMap();

	public void setPartialsCore(PartialsCore partials) {
		this.partialsCore = partials;
	}	
	
	public void setRequest(HttpServletRequest request) {
		this.request = request;
	}

	public void setResponse(HttpServletResponse response) {
		this.response = response;
	}

	public void setModel(Map model) {
		this.model = model;
	}

	public void buildJson(List<List> jsonWorkRequests){
		for(List request : jsonWorkRequests){
			Short command = (Short) request.get(0);
			List list = (List) request.get(1);
			int index = (Integer) request.get(2);
			Object obj = list.get(index);
			switch (command) {
	            case JsonAjax.AD_HOC_ADDITION:  handleAdHocAddition(obj); break;
	            case JsonAjax.JAVASCRIPT_VIA_VIEW:  
	            	String jsonKey = JsonAjax.JAVASCRIPT_KEY;
	            	String viewName = (String) obj;
	            	
	            	//TODO IMPORTANT!! For now we can't render a javascript partial
	            	//with a modelOverride.  Probably best if we get rid of the idea
	            	//of a specific method where we add the 'javascript' key, and we
	            	//just let people who are creating a javascript partial provide that
	            	//key themselves.  The disadvantage is that we would no longer conglomerate
	            	//the results of different javascript keys into one long javascript.
	            	//THE OFFENDING CODE is where we add the new HashMap into the partialsList here.
	            	//Perhaps we end up allowing developers to specify when values for a key are 
	            	//to be concatenated rather than overwritten.
	        		List partialsList  = new ArrayList(3);
	        		partialsList.add(jsonKey);
	        		partialsList.add(viewName);
	        		partialsList.add(new HashMap());
	        		Map weOnlyWantTheSingleValueInThisMap = renderPartial(partialsList);
	        		addJavaScript((String) weOnlyWantTheSingleValueInThisMap.get(jsonKey)); 
	            	
	            break;
	            case JsonAjax.JAVASCRIPT_VIA_STRING:  addJavaScript((String) obj); break;
	            case JsonAjax.RENDER_A_PARTIAL:  this.willBeJson.putAll(renderPartial(obj)); break;
	            default: throw new IllegalArgumentException(command + " is not supported.");
			}
		}
		if(! "".equals(this.javascript.toString()) ){
			willBeJson.put(JsonAjax.JAVASCRIPT_KEY, this.javascript.toString());
		}
	}
	
	protected void addJavaScript(String more){
		this.javascript.append(more);
	}
	
	protected void handleAdHocAddition(Object obj) {
		List keyAndValue = (List) obj;
		willBeJson.put(keyAndValue.get(0), keyAndValue.get(1));
	}
	
	protected Map renderPartial(Object obj){
		List partialsList  = (List) obj;
		
		List<String> jsonKeys = new ArrayList<String>();
		jsonKeys.add((String) partialsList.get(0));
		List<String> viewNames = new ArrayList<String>();
		viewNames.add((String) partialsList.get(1));
		
		//TODO  IMPORTANT Probably a good idea to save the original models
		//of attributes, replace and put them back after using the partialsCore.
		Map modelOverride = (Map) partialsList.get(2);
		for(Object key : modelOverride.keySet()){
			this.model.put(key, modelOverride.get(key));
		}
		
		
		return this.partialsCore.makeMap(jsonKeys, viewNames, this.model,
				this.request, this.response);
		
	}
	
	
	public void writeJson(){
		partialsCore.write(response, toJson());
	}	
	
	protected String toJson(){
		return partialsCore.getJsonSerializer().mapToJson(this.willBeJson);
	}
	
	protected void finalize() throws Throwable {
		this.partialsCore = null;
		this.request = null;
		this.response = null;
		this.model = null;
		super.finalize();
	}	
	

}
